package stateselector;


import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.LinkedList;
import java.util.Queue;

import javax.lang.model.element.NestingKind;
import javax.swing.Timer;

import connection.ControllerConnectionIn;
import connection.ControllerConnectionOut;
import connection.ControllerMessage;
import connection.LightStates;
import connection.Relay;
import connection.SensorMessage;

import kruispunt.Kruispunt;

public class StateSelector {
	private static final double MUL = 1.5; //Versnelling
	private Relay relay;
	private Kruispunt kp;
	private State A1,A2,AV1,AV2,AV3,AV4,F,OFF; 
	private State currentState;
	private Timer teller;
	private Queue<State> statenlijst = new LinkedList<State>();
	private Integer[] prioriteiten = {0,0,0,0,0,0,0,0};
	private boolean orange = false;
	
	
	public StateSelector(Kruispunt kp) {
		System.out.println("MUL = " + MUL + ". Tikken zijn: " + ((15000.0 / MUL)/1000.0) + " Sec. Max tikken groen is: " + (12/MUL)); 
		relay = new Relay();
		this.kp = kp;
		// maak de verschillende staten aan
		A1 = new State("A1", Lights.A1,kp);
		A2 = new State("A2", Lights.A2,kp);
		AV1 = new State("AV1", Lights.AV1,kp);
		AV2 = new State("AV2", Lights.AV2,kp);
		AV3 = new State("AV3", Lights.AV3,kp);
		AV4 = new State("AV4", Lights.AV4,kp);
		F = new State("F", Lights.F,kp);
		OFF = new State("OFF", Lights.OFF,kp);
		statenlijst.clear();
		statenlijst.add(A1);
		System.out.println("Creating Sockets");
		teller = new Timer((int) (15000 / MUL), new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				timerTrigger();	
			}
		});
		createSocket();
	}
	
	private void timerTrigger() {
		addBaanTurns();
		currentState.addTurn();
		System.out.println("TIK! " + currentState.getTurns());
		updatePriorities();
		for (int i = 1; i < (prioriteiten.length-1); i++) {
			System.out.print(getState(i).getName() + ":" + prioriteiten[i].toString() + " || ");
		}
		System.out.println(getState(7).getName() + ":" + prioriteiten[7].toString());

		if (!orange){ //Hier de uitzonderingen
			if (currentState.getTurns() >= (12 / MUL) ) {
				System.out.println(currentState.getName() + " is over it's max time");
				endCurrentState();	// 3 Minuten(12 tikken) is de max Groentijd!
			}
			if (currentState.getStatePriority() <= 8) {
				System.out.println(currentState.getName() + " has priority <= 8");				
				endCurrentState();
			}
		} else { // Switch naar de volgende state als het licht op oranje staat
			sendState(LightStates.RED); //zet huidige state op rood
			if (!statenlijst.isEmpty()) {
				currentState = statenlijst.poll(); // volgende State
				System.out.println("Switched to state " + currentState.getName());
				sendState(LightStates.GREEN);	//Stuur de nieuwe staat door naar de Sim
			} else {
				teller.stop();
				System.out.println("Lege statenlijst!");
			}
			orange = false;
		}
	}

	private void addBaanTurns() {
		kp.addTurn(0);		
		kp.addTurn(4);		
		kp.addTurn(5);		
		kp.addTurn(6);		
		kp.addTurn(7);		
		kp.addTurn(11);		
		kp.addTurn(12);		
		kp.addTurn(13);		
		kp.addTurn(14);		
		kp.addTurn(18);		
		kp.addTurn(19);		
		kp.addTurn(20);		
		kp.addTurn(21);		
		kp.addTurn(25);		
		kp.addTurn(26);		
		kp.addTurn(27);		
	}

	private void endCurrentState() {
		System.out.println("Ending state " + currentState.getName());
		sendState(LightStates.ORANGE);	//Stuur oranje puls
		int highestPrio = 0;
		int prioState = 0;
		int nextPriostate = 0;
		for (int i=1; i < prioriteiten.length;i++) {
			if (prioriteiten[i] >= highestPrio) {
				highestPrio = prioriteiten[i];
				nextPriostate = prioState;
				prioState = i;
			}
		}
		if (currentState != getState(prioState)) {
			statenlijst.add(getState(prioState));
			System.out.println("Staat " + getState(prioState).getName() + " toegevoegd!");			
		} 
		else {
			if (nextPriostate != 0) {
				statenlijst.add(getState(nextPriostate));
				System.out.println("**Staat " + getState(nextPriostate).getName() + " toegevoegd!");										
			} else 	{
				statenlijst.add(getState(prioState));
				System.out.println("**Staat " + getState(prioState).getName() + " toegevoegd!");										
			}
 
		}
		currentState.resetTurns();
		orange = true;
	}

	private void sendState(int state) {
		for (int i = 0; i < currentState.getGreenlights().length; i++) {
			int[] greenLights = currentState.getGreenlights();
			int light = greenLights[i];
			kp.resetBaan(light);
			relay.addControllerMessage(new ControllerMessage(light, state)); //Stoplicht Rood
		}
	}


	public State getState(int stateID) {
		if (stateID == 1) { return A1;}
		if (stateID == 2) { return A2;}
		if (stateID == 3) { return AV1;}
		if (stateID == 4) { return AV2;}
		if (stateID == 5) { return AV3;}
		if (stateID == 6) { return AV4;}
		if (stateID == 7) { return F;}
		else { return null; }
	}
	

	/**
	 * Update de Prioriteit van alle staten en slaat dezen op in een HashTable
	 * 
	 * Hieruit kan vervolgens de hoogste prioriteit gepakt worden en wanneer nodig als volgende geplaatst worden.
	 * 
	 */
	public void updatePriorities() {
		prioriteiten[1] = A1.getStatePriority();
		prioriteiten[2] = A2.getStatePriority();
		prioriteiten[3] = AV1.getStatePriority();
		prioriteiten[4] = AV2.getStatePriority();
		prioriteiten[5] = AV3.getStatePriority();
		prioriteiten[6] = AV4.getStatePriority();
		prioriteiten[7] = F.getStatePriority();
	}

	
	public void createSocket() {
        try {
            ServerSocket ssckt = new ServerSocket(11145);
            Socket socket = ssckt.accept();
            
            System.out.println("Socket is open...");
            
            ControllerConnectionIn connIn = new ControllerConnectionIn(socket, relay);
            ControllerConnectionOut connOut = new ControllerConnectionOut(socket, relay);
            Thread connInThread = new Thread(connIn);
            Thread connOutThread = new Thread(connOut);
            connInThread.start();
            connOutThread.start();
            
            System.out.println("Input and Output threads running...");
    		currentState = statenlijst.poll();
    		teller.start();
    		sendOffState();
            boolean msgReceived = false;
            while (!msgReceived) {
                    SensorMessage msg = relay.getSensorMessage();
                    if (msg != null) {
                            msgReceived = true;
                            incomingMsg(msg.getLane(), msg.getSensor());
                            msgReceived=false;
                    }
                    else {
                            try {
                                    Thread.sleep(100);
                            } 
                            catch (InterruptedException e) {
                                    e.printStackTrace();
                            }
                    }
            }
    } 
    catch (IOException e) {
    	e.printStackTrace();
    }
	}
	
	private void sendOffState() {
		currentState=OFF;
		currentState.setTurns(20);
	}

	private void incomingMsg(int lane, int sensor) {
		if (sensor == 1) { //100 meter lus
			kp.doTrigger(lane);
		} else if (sensor == 2) { //1 meter lus			
			kp.verwijderAuto(lane);
		} else {
			System.out.println("invalid sensorID" );
		}
	}	
}
